package distributed.lock;

import annotation.ThreadSafe;
import distributed.lock.redis.JedisClientRedisHandle;
import distributed.lock.redis.JedisRedisHandleAdapter;
import distributed.lock.redis.RedisHandle;
import distributed.lock.redis.RedisHandleAdapter;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@ThreadSafe
public class RedisReentrantLock implements RedisLock {
    RedisHandle redisHandle = new JedisClientRedisHandle();
    RedisHandleAdapter handleAdapter = new JedisRedisHandleAdapter();
    ReentrantLock reentrantLock = new ReentrantLock();
    private static ThreadLocal<String> randomValue = new ThreadLocal<>();
    int expire = 1000;

    private String currentValue() {
        String s = randomValue.get();
        if (s != null) {
            return s;
        }
        String r;
        randomValue.set(r = UUID.randomUUID().toString());
        return r;
    }

    @Override
    public boolean tryLock(String key, long timeOut, TimeUnit unit) throws InterruptedException {
        final long deadline = System.nanoTime() + unit.toNanos(timeOut);
        boolean b = reentrantLock.tryLock(timeOut, unit);
        //超时获取 jvm 锁失败了
        if (!b) return false;
        int holdCount = reentrantLock.getHoldCount();
        //可重入
        if (holdCount > 1) {
            return true;
        }
        try {
            //自旋
            while (true) {
                //分布式锁获取成功 redis
                if (handleAdapter.setnx(key, currentValue(), expire, TimeUnit.SECONDS, redisHandle)) {
                    return true;
                }
                //超时
                timeOut = deadline - System.nanoTime();
                if (timeOut <= 0L) {
                    reentrantLock.unlock();
                    return false;
                }
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
            //出异常释放锁
            reentrantLock.unlock();
            return false;
        }
    }

    @Override
    public void unlock(String key) {
        reentrantLock.unlock();
        int holdCount = reentrantLock.getHoldCount();
        if (holdCount > 0) {
            return;
        }
        String s = randomValue.get();
        randomValue.remove();
        boolean b = handleAdapter.comparAndDel(key, s, redisHandle);
        if (!b) System.out.println("释放redis锁失败了");
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        throw new IllegalMonitorStateException();
    }


    @Override
    public void unlock() {
        throw new IllegalMonitorStateException();
    }


}
